package middleware

import (
	"crypto/sha1"
	"encoding/hex"
	"errors"
	"math/rand"
	"strconv"
	"time"
)

var Keys map[string]string

type Signature struct {
	AppKey   string `json:"appkey"`
	CurTime  string `json:"curtime"`
	Nonce    string `json:"nonce"`
	CheckSum string `json:"checksum"`
}

func GenerateSignature(key string, secret string) *Signature {
	curTime := strconv.FormatInt(getUnixTimestamp(), 10)
	nonce := getRandomString(16)
	checkSum := getSha1Str(secret + nonce + curTime)
	return &Signature{
		AppKey:   key,
		CurTime:  curTime,
		Nonce:    nonce,
		CheckSum: checkSum,
	}
}

func (sign *Signature) CheckSignature() error {
	if "" == sign.AppKey || "" == sign.CurTime || "" == sign.Nonce || "" == sign.CheckSum {
		return errors.New("verification Sign is empty")
	}
	if nil == Keys {
		return errors.New("verification AppKey failed")
	}
	secret, ok := Keys[sign.AppKey]
	if false == ok {
		return errors.New("verification AppKey failed")
	}
	curTime, err := strconv.ParseInt(sign.CurTime, 10, 64)
	if nil != err {
		return errors.New("verification CurTime failed")
	}
	if getUnixTimestamp()-300 > curTime {
		return errors.New("verification CurTime failed")
	}
	CheckSum := getSha1Str(secret + sign.Nonce + sign.CurTime)
	if CheckSum != sign.CheckSum {
		return errors.New("verification CheckSum failed")
	}
	return nil
}

func getUnixTimestamp() int64 {
	return time.Now().Unix()
}

func getSha1Str(str string) string {
	h := sha1.New()
	h.Write([]byte(str))
	return hex.EncodeToString(h.Sum(nil))
}

func getRandomString(l int) string {
	str := "0123456789abcdefghijklmnopqrstuvwxyz"
	bytes := []byte(str)
	result := []byte{}
	r := rand.New(rand.NewSource(time.Now().UnixNano()))
	for i := 0; i < l; i++ {
		result = append(result, bytes[r.Intn(len(bytes))])
	}
	return string(result)
}
